import * as THREE from '../../build/three.module.js';
import { GUI } from '../../jsm/libs/dat.gui.module.js';
import Stats from '../../jsm/libs/stats.module.js';
import { OrbitControls } from '../../jsm/controls/OrbitControls.js';

const r = 800,
      rHalf = r / 2,
      maxParticleCount = 1000, // 最大粒子数
      particlesData = []; // 粒子冻动画数据


let particleCount = 500; // 渲染粒子数

const effectController = {
  showDots: true,
  showLines: true,
  minDistance: 150,
  limitConnections: false,
  maxConnections: 20,
  particleCount: 500
};

const scene = new THREE.Scene();
scene.background = new THREE.Color(0x050505);

const camera = new THREE.PerspectiveCamera(65, innerWidth / innerHeight, 1, 10000);
camera.position.z = 1750;

const group = new THREE.Group();
scene.add(group);

// 立方体
const boxHelper = new THREE.BoxHelper(new THREE.Mesh( new THREE.BoxBufferGeometry(r, r, r) ))
boxHelper.material.color.setHex(0x101010);
boxHelper.material.blending = THREE.AdditiveBlending;
boxHelper.material.transparent = true;
group.add(boxHelper);

// 构建点组
const pGmt = new THREE.BufferGeometry();
const pGmtPosition = new Float32Array(maxParticleCount * 3);

const pMtl = new THREE.PointsMaterial({
  color: 0xffffff,
  size: 3,
  blending: THREE.AdditiveBlending,
  transparent: true,
  sizeAttenuation: false
})

for(let i = 0; i < maxParticleCount; i += 1) {
  const x = Math.random() * r - r / 2;
  const y = Math.random() * r - r / 2;
  const z = Math.random() * r - r / 2;

  pGmtPosition[i * 3] = x;
  pGmtPosition[i * 3 + 1] = y;
  pGmtPosition[i * 3 + 2] = z;

  // 添加动画数据
  particlesData.push({
    velocity: new THREE.Vector3(-1 + Math.random() * 2, -1 + Math.random() * 2, -1 + Math.random() * 2),
    numConnections: 0
  })
}

pGmt.setDrawRange(0, particleCount);
pGmt.setAttribute('position', new THREE.BufferAttribute(pGmtPosition, 3).setUsage(THREE.DynamicDrawUsage));

const pointCloud = new THREE.Points(pGmt, pMtl);
group.add(pointCloud);

// 构建连线
const segments = maxParticleCount * maxParticleCount;
const positions = new Float32Array(segments * 3);
const colors = new Float32Array(segments * 3);

const lGmt = new THREE.BufferGeometry();
lGmt.setAttribute('position', new THREE.BufferAttribute(positions, 3).setUsage(THREE.DynamicDrawUsage));
lGmt.setAttribute('color', new THREE.BufferAttribute(colors, 3).setUsage(THREE.DynamicDrawUsage));
lGmt.computeBoundingSphere();
lGmt.setDrawRange(0, 0);
const lMtl = new THREE.LineBasicMaterial({
  vertexColors: THREE.VertexColors,
  blending: THREE.AdditiveBlending,
  transparent: true
});
const lineMesh = new THREE.LineSegments(lGmt, lMtl);
group.add(lineMesh);

const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(devicePixelRatio);
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);

new OrbitControls(camera, renderer.domElement);

const stats = new Stats();
document.body.appendChild(stats.dom);

const render = () => {
  const time = Date.now() * .001;

  group.rotation.y = time * .1;
  renderer.render(scene, camera);
}

const animate = () => {
  let vertexpos = 0,
      colorpos = 0,
      numConncted = 0;
  
  for(let i = 0; i < particleCount; i += 1)
    particlesData[i].numConnections = 0;

  for(let i = 0; i < particleCount; i += 1) {
    const particleData = particlesData[i];
    pGmtPosition[i * 3] += particleData.velocity.x;
    pGmtPosition[i * 3 + 1] += particleData.velocity.y;
    pGmtPosition[i * 3 + 2] += particleData.velocity.z;

    if (pGmtPosition[i * 3 + 1] < -rHalf || pGmtPosition[i * 3 + 1] > rHalf)
      particleData.velocity.y = -particleData.velocity.y;

    if (pGmtPosition[i * 3] < -rHalf || pGmtPosition[i * 3] > rHalf)
      particleData.velocity.x = - particleData.velocity.x;

    if (pGmtPosition[i *3 + 2] < -rHalf || pGmtPosition[i *3 + 2] > rHalf)
      particleData.velocity.z = - particleData.velocity.z;

    // 限制链接数
    if (effectController.limitConnections && particleData.numConnections >= effectController.maxConnections)
      continue;

    // 处理连线
    for(let j = i + 1; j < particleCount; j += 1) {
      const particleDataB = particlesData[j];

      if (effectController.limitConnections && particleDataB.numConnections >= effectController.maxConnections)
        continue;

      const dx = pGmtPosition[i * 3] - pGmtPosition[j * 3];
      const dy = pGmtPosition[i * 3 + 1] - pGmtPosition[j * 3 + 1];
      const dz = pGmtPosition[i * 3 + 2] - pGmtPosition[j * 3 + 2];
      const dist = Math.sqrt( dx * dx + dy * dy + dz * dz );

      if (dist < effectController.minDistance) { // 小于最小长度开始绘制联系那
        particleData.numConnections++;
        particleDataB.numConnections++;

        const alpha = 1 - (dist / effectController.minDistance);

        // 连线的两个点
        positions[vertexpos++] = pGmtPosition[i * 3];
        positions[vertexpos++] = pGmtPosition[i * 3 + 1];
        positions[vertexpos++] = pGmtPosition[i * 3 + 2];
        
        positions[vertexpos++] = pGmtPosition[j * 3];
        positions[vertexpos++] = pGmtPosition[j * 3 + 1];
        positions[vertexpos++] = pGmtPosition[j * 3 + 2];

        colors[colorpos++] = alpha;
        colors[colorpos++] = alpha;
        colors[colorpos++] = alpha;

        colors[colorpos++] = alpha;
        colors[colorpos++] = alpha;
        colors[colorpos++] = alpha;

        numConncted++;
      }
    }
  }

  lineMesh.geometry.setDrawRange(0, numConncted * 2);
  lineMesh.geometry.attributes.position.needsUpdate = true;
  lineMesh.geometry.attributes.color.needsUpdate = true;

  pointCloud.geometry.attributes.position.needsUpdate = true;

  requestAnimationFrame(animate);

  stats.update();
  render();
}
animate();

// 构建Dat
var gui = new GUI();

gui.add( effectController, "showDots" ).onChange( function ( value ) {

  pointCloud.visible = value;

} );
gui.add( effectController, "showLines" ).onChange( function ( value ) {

  lineMesh.visible = value;

} );
gui.add( effectController, "minDistance", 10, 300 );
gui.add( effectController, "limitConnections" );
gui.add( effectController, "maxConnections", 0, 30, 1 );
gui.add( effectController, "particleCount", 0, maxParticleCount, 1 ).onChange( function ( value ) {

  particleCount = parseInt( value );
  pGmt.setDrawRange( 0, particleCount );

} );

const handleWindowResize = () => {
  camera.aspect = innerWidth / innerHeight;
  camera.updateProjectionMatix();

  renderer.setSize(innerWidth, innerHeight);
}
window.addEventListener('resize', handleWindowResize, false);